/**
 * @author Gragory Banfi
 * @author Virginie Blancs
 * @author Fatima Chegini
 * @author Nicolas Latorre
 * @author Giuseppe Mendola
 * @author Gabriele Cerfoglio
 */

package sa4

import javax.servlet.http._
import scala.xml._
import javax.servlet.ServletConfig
import sa4.form._
import sa4.session._
import sa4.template._
import sa4.routing._
import sa4.persistence._
import org.squeryl.KeyedEntity
import org.squeryl.SessionFactory
import org.squeryl.Session
import org.squeryl.adapters.H2Adapter
import scala.util.matching.Regex
import org.squeryl.adapters.H2Adapter
import java.sql.Timestamp
import java.sql.Date
import org.squeryl.PrimitiveTypeMode._
import org.squeryl.dsl.ast.LogicalBoolean
import org.squeryl.annotations.OptionType

// This is a very simple Scala servlet.
// If you rename this class, you must also change the file
// src/main/webapp/WEB-INF/web.xml to use the new name.
class FrameworkServlet extends HttpServlet {
  type HandlerFunction = (HttpServletRequest, HttpServletResponse) => Unit
  val routing = new Routing()
  val template = new Template()
  val session = new SessionManagement()
  val persistence = new Persistence()

  /**
   * Function that initializes the servlet
   * @param config Configuration for the servlet
   */
  override def init(config: ServletConfig) {
    super.init(config)
    //Starting the database
    Class.forName("org.h2.Driver")

    SessionFactory.concreteFactory = Some(() =>
      Session.create(
        java.sql.DriverManager.getConnection("jdbc:h2:" + System.getProperty("user.dir") + "/test"),
        new H2Adapter))

    transaction {
      Bank.drop
    }

    transaction {
      Bank.create
    }

    transaction {
      Bank.accounts.insert(new Account("Someone", 333))
      Bank.accounts.insert(new Account("Steve Jobs", 984))
    }

    //Adding some route
    routing.addRoute("/start", startPage)
    routing.addRoute("/index.css", css)
    routing.addRoute("/display", displayForm)
    routing.addRoute("/", session.justSession)
    routing.addRoute("/listAccount", getAccounts)
    routing.addRoute("/newAccount", getDataOfUser)
    routing.addRoute("/deleteAccount", getDeletePage)
    routing.addRoute("/updateAccount", getUpdatePage)
    routing.addRoute("/listMouvement", getMouvement)
    routing.addRoute("/newAccount", getDataOfUser)
  }

  /**
   * Function that manages requests and response on the server
   * @param req Request from the servlet
   * @param resp Response to the servlet
   */
  override def service(req: HttpServletRequest, resp: HttpServletResponse) {
    //Session setup
    var currentContext = session.getCurrentContext
    currentContext = session.newContext(req, resp, currentContext)
    currentContext().session.setMaxInactiveInterval(10)

    //Routing dispatch
    if (routing.tableDispatch(req, resp))
      return
    resp.sendError(HttpServletResponse.SC_NOT_FOUND)

  }

  def startPage(req: HttpServletRequest, resp: HttpServletResponse) {
    session.updateSession
    val xml = template.loadPage("index.html")
    val html = prepareForm(xml)
    resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
    resp.setContentType("text/html")
  }

  def css(req: HttpServletRequest, resp: HttpServletResponse) {
    val css = template.loadCss("index.css")
    resp.getWriter.write(css)
    resp.setContentType("text/css")
  }

  def getDataOfUser(req: HttpServletRequest, resp: HttpServletResponse) {
    session.updateSession
    val ns = template.setUpTemplate("templateNewAccount.xml")
    val html = prepareForm(ns)
    resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
  }

  def getDeletePage(req: HttpServletRequest, resp: HttpServletResponse) {
    session.updateSession
    val ns = template.setUpTemplate("templateDeleteAccount.xml")
    val html = prepareForm(ns)
    resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
  }

  def getUpdatePage(req: HttpServletRequest, resp: HttpServletResponse) {
    session.updateSession
    val ns = template.setUpTemplate("templateUpdateAccount.xml")
    val html = prepareForm(ns)
    resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
  }

  def getAccounts(req: HttpServletRequest, resp: HttpServletResponse) {
    session.updateSession
    val xml = accountsPage
    val html = prepareForm(xml)
    resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
  }

  def getMouvement(req: HttpServletRequest, resp: HttpServletResponse) {
    session.updateSession
    val xml = mouvementPage
    val html = prepareForm(xml)
    resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
  }

  //FORM
  def displayForm(req: HttpServletRequest, resp: HttpServletResponse) = {
    session.updateSession
    val tag: Tag = new Tag("form", List(("method", "post"), ("handler", "postComment(author, content)")), "")
    val tag2: Tag = new Tag("input", List(("type", "text"), ("name", "author")), "Name: ")
    val tag3: Tag = new Tag("textarea", List(("name", "content"), ("rows", "4"), ("cols", "50")), "Content: ")
    val tag4: Tag = new Tag("input", List(("type", "submit"), ("value", "Submit your comment")), "")
    val form = new Form(List(tag, tag2, tag3, tag4))
    val ns = <html><body>{ form.makeForm }</body></html>
    val html = prepareForm(ns)
    resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
  }

  /**
   * Prepare the form
   * @param template The template of the form we are using
   * @return NodeSeq The template changed
   */
  def prepareForm(template: NodeSeq): NodeSeq = {
    import scala.xml.transform._
    import scala.xml.Null // hide scala.Null
    object MakeForm extends RewriteRule {
      override def transform(n: Node): Seq[Node] = n match {
        case form: Elem if form.label == "form" =>
          val Some(handler) = form.attribute("handler")
          var Some(method) = form.attribute("method")

          // Make the handler and install in routeTable
          val (uri, f: HandlerFunction) = makeHandler(handler.text)

          routing.addRoute(uri, f)

          // Add an action to the form.
          form % Attribute(None, "action", Text(uri), Null)
        case n => n
      }
    }
    template.map(ns => new RuleTransformer(MakeForm)(ns))
  }

  /**
   * Creates a new path with its corresponding function
   * @param s
   * @return String A new path
   * @return HandlerFunction The corresponding function
   */
  def makeHandler(s: String): (String, HandlerFunction) = {
    // Parse the handler string.
    val q = "^(\\w+)\\((.*)\\)$".r

    val (methodName, argNames) = s match {
      case q(methodName, argNames) => (methodName, argNames.split(",").map(_.trim))
      case _ => throw new RuntimeException("bad handler: " + s)
    }

    // Look for a method that takes the right number of strings.
    val method = this.getClass.getMethod(methodName, argNames.map(_ => classOf[String]): _*)
    val h = (req: HttpServletRequest, resp: HttpServletResponse) => {
      val result = method.invoke(this, argNames.map(k => req.getParameter(k)): _*)
      result match {
        case s: String =>
          resp.getWriter.write(s)
        case html: NodeSeq =>
          resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
      }
    }
    (handlerURI(methodName), h)
  }

  /**
   * Constructs a new path with the method name
   * @param methodName The method that will be used as path
   */
  def handlerURI(methodName: String) = {
    "/" + methodName
  }

  /**
   * Function that displays a page in response to the post in the form
   * The page displayed will contain the author name and the message that have been passed to the form
   * @param author The name of the author of the post
   * @param content The message written in the post
   */
  def postComment(author: String, content: String) =
    <html>
      <head>
        <title>Form submitted</title>
      </head>
      <body>
        <h3>Form submitted</h3>
        <p>{ author } submitted the following comment:</p>
        <blockquote>{ content }</blockquote>
      </body>
    </html>

  /*
   * *************persistence*******************
   */

  def accountsPage = {
    <html>
      <head>
        <title>Accounts:</title>
      </head>
      <body>
        <h3>All accounts</h3>
        <table border="1">
          <tr>
            <th>Name</th>
            <th>Balance</th>
          </tr>
          {
            persistence.getAccounts
          }
        </table>
      </body>
    </html>
  }

  def mouvementPage = {
    <html>
      <head>
        <title>Accounts:</title>
      </head>
      <body>
        <h3>All mouvements</h3>
        <table border="1">
          <tr>
            <th>Name</th>
            <th>Amount</th>
            <th>Date</th>
            <th>Mouvement</th>
          </tr>
          {
            persistence.getMouvements
          }
        </table>
      </body>
    </html>
  }

  def postData(name: String, balance: String) =
    <html>
      <head>
        <title> Data Sent Page </title>
      </head>
      <body>
        {
          persistence.newAccount(name, balance)
        }
        <h3>Data Added</h3>
      </body>
    </html>

  def deleteData(name: String) =
    <html>
      <head>
        <title> Data Sent Page </title>
      </head>
      <body>
        {
          persistence.deleteAccount(name)
        }
        <h3>Data Added</h3>
      </body>
    </html>

  def updateData(name: String, balance: String) =
    <html>
      <head>
        <title> Data Sent Page </title>
      </head>
      <body>
        {
          persistence.updateAccount(name, balance)
        }
        <h3>Data Added</h3>
      </body>
    </html>
}